1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use crate::generate_property_enums;
use crate::sketchbook::ids::{DatasetId, ObservationId};
use crate::sketchbook::properties::HctlFormula;
use crate::sketchbook::JsonSerde;
use serde::{Deserialize, Serialize};
use std::mem::discriminant;

/// Variant of `DynProperty` requiring that a particular HCTL formula is satisfied.
/// The struct carries the user-provided `raw_formula` string, as well as its processed
/// internal version `processed_formula`.
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct GenericDynProp {
    pub raw_formula: String,
    pub processed_formula: HctlFormula,
}

/// Variant of `DynProperty` requiring existence of a fixed point corresponding to
/// a particular `observation` of a particular `dataset`.
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct ExistsFixedPoint {
    pub dataset: Option<DatasetId>,
    pub observation: Option<ObservationId>,
}

/// Variant of `DynProperty` requiring existence of a trap space corresponding to
/// a particular `observation` of a particular `dataset`.
/// Optionally, the required trap space might be required to be `minimal` or `non-percolable`.
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct ExistsTrapSpace {
    pub dataset: Option<DatasetId>,
    pub observation: Option<ObservationId>,
    pub minimal: bool,
    pub nonpercolable: bool,
}

/// Variant of `DynProperty` requiring existence of a trajectory between observations
/// of a particular `dataset` (in a given order).
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct ExistsTrajectory {
    pub dataset: Option<DatasetId>,
}

/// Variant of `DynProperty` requiring that the number of attractors falls into the range
/// <minimal, maximal>.
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct AttractorCount {
    pub minimal: usize,
    pub maximal: usize,
}

/// Variant of `DynProperty` requiring one of the following (depending on whether `observation`
/// is specified or not):
/// 1) all observations of a particular dataset correspond to an attractor
/// 2) a particular `observation` of a particular `dataset` correspond to an attractor
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct HasAttractor {
    pub dataset: Option<DatasetId>,
    pub observation: Option<ObservationId>,
}

// Two versions of the enum to cover all variants of the dynamic properties.
// One contains the property data inside, the other one only the discriminants.
generate_property_enums!(
    /// Enum covering all variants of dynamic properties and their necessary data.
    DynPropertyType,
    /// Enum covering all variants of dynamic properties (only discriminants, no data).
    SimpleDynPropertyType, {
        GenericDynProp(GenericDynProp),
        ExistsFixedPoint(ExistsFixedPoint),
        ExistsTrapSpace(ExistsTrapSpace),
        ExistsTrajectory(ExistsTrajectory),
        AttractorCount(AttractorCount),
        HasAttractor(HasAttractor)
    }
);

impl<'de> JsonSerde<'de> for SimpleDynPropertyType {}

/// Check if two DynPropertyType instances are of the same variant.
pub fn are_same_dyn_variant(a: &DynPropertyType, b: &DynPropertyType) -> bool {
    discriminant(a) == discriminant(b)
}